/*
 * Copyright (C) 2010-2025 Structr GmbH
 *
 * This file is part of Structr <http://structr.org>.
 *
 * Structr is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * Structr is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Structr.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.structr.core.function;

import org.apache.commons.lang3.StringUtils;
import org.structr.common.error.FrameworkException;
import org.structr.docs.Example;
import org.structr.docs.Parameter;
import org.structr.docs.Signature;
import org.structr.docs.Usage;
import org.structr.schema.action.ActionContext;

import java.text.NumberFormat;
import java.text.ParseException;
import java.util.List;
import java.util.Locale;

public class ParseNumberFunction extends CoreFunction {

	@Override
	public String getName() {
		return "parseNumber";
	}

	@Override
	public List<Signature> getSignatures() {
		return Signature.forAllScriptingLanguages("number [, locale ]");
	}

	@Override
	public Object apply(final ActionContext ctx, final Object caller, final Object[] sources) throws FrameworkException {

		if (sources == null || (sources.length == 2 && sources[1] == null) || sources.length > 2) {
			logParameterError(caller, sources, ctx.isJavaScriptContext());
			return usage(ctx.isJavaScriptContext());
		}

		try {

			final String numberString = sources[0].toString().replaceAll("[^\\d.,-]", "");

			if (StringUtils.isBlank(numberString)) {
				return "";
			}

			Locale locale = ctx.getLocale();
			try {

				if (sources.length == 2) {

					final String localeString = sources[1].toString();

					if (StringUtils.isNotBlank(localeString)) {

						locale = Locale.forLanguageTag(localeString);
					}
				}

				return NumberFormat.getInstance(locale).parse(numberString);

			} catch (ParseException ex) {

				logException(ex, "{}: Could not parse string \"{}\" to number. Parameters: {}", new Object[] { getDisplayName(), caller, getParametersAsString(sources) });
			}

		} catch (final IllegalArgumentException e) {

			logParameterError(caller, sources, ctx.isJavaScriptContext());

			return usage(ctx.isJavaScriptContext());

		}

		return "";
	}

	@Override
	public List<Usage> getUsages() {
		return List.of(
				Usage.structrScript("Usage: ${parseNumber(value, locale)}."),
				Usage.javaScript("Usage: ${{ $.parseNumber(value, locale) }}.")
		);
	}

	@Override
	public String getShortDescription() {
		return "Parses the given string using the given (optional) locale.";
	}

	@Override
	public String getLongDescription() {
		return "Parses the given string into a numerical value. With the second (optional) parameter you can pass a locale string to take a country-/language specific number formatting into account.";
	}

	@Override
	public List<Example> getExamples() {
		return List.of(
				Example.structrScript("${parseNumber('123,456,789.123', 'en')}"),
				Example.structrScript("${parseNumber('123.456.789,123', 'de')}"),
				Example.javaScript("${{ $.parseNumber('123,456,789.123', 'en') }}"),
				Example.javaScript("${{ $.parseNumber('123.456.789,123', 'de') }}")
		);
	}

	@Override
	public List<String> getNotes() {
		return List.of(
				"If no locale parameter is given, the default locale for the context is used. See the `locale` keyword."
		);
	}

	@Override
	public List<Parameter> getParameters() {

		return List.of(
				Parameter.mandatory("string", "String that will be parsed into numerical value"),
				Parameter.optional("locale", "Locale string for specific number formatting")
		);
	}
}
